package hust.hiepdv.javaproject.view;

import hust.hiepdv.javaproject.constant.Constant;
import hust.hiepdv.javaproject.model.Ball;
import hust.hiepdv.javaproject.model.DrawableObject;
import hust.hiepdv.javaproject.model.Pad;
import hust.hiepdv.javaproject.model.Player;
import hust.hiepdv.javaproject.model.Table;

import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.JPanel;

public class DrawPanel extends JPanel implements Constant, Runnable{

	private BufferedImage bufferedImage;
	
	private Table table;
	private Ball ball;
	private ArrayList<DrawableObject> drawObjs = new ArrayList<DrawableObject>();
	private Pad pad1, pad2, pad3, pad4;
	private ArrayList<Pad> pads = new ArrayList<Pad>();	
	boolean moveRight = false;
	boolean moveLeft = false;
	Player player1;
	Player player2;
	Player player3;
	Player player4;
	ServerView serverView;
	private boolean isPlaying = false;
	
	public DrawPanel(ServerView serverView) {
		setFocusable(true);
		addKeyListener(keyAdapter);
		this.ball = ServerView.ball;
		this.pad1 = ServerView.pad1;
		this.pad2 = ServerView.pad2;
		this.pad3 = ServerView.pad3;
		this.pad4 = ServerView.pad4;
		
		this.serverView = serverView;
		this.player1 = ServerView.player1;
		this.player2 = ServerView.player2;
		this.player3 = ServerView.player3;
		this.player4 = ServerView.player4;
	}

	public synchronized void initialize() {
		bufferedImage = new BufferedImage(600,
				600, BufferedImage.TYPE_INT_RGB);

		table = new Table(0, 0, Table.STYLE_1);
		drawObjs.add(table);

		//pad1 = new Pad(200, 40, Pad.STYLE_1, Pad.HORIZONTAL);		
		drawObjs.add(pad1);
		pads.add(pad1);
		//pad2 = new Pad(550, 100, Pad.STYLE_2, Pad.VERTICAL);
		drawObjs.add(pad2);
		pads.add(pad2);
		//pad3 = new Pad(100, 550, Pad.STYLE_3, Pad.HORIZONTAL);
		drawObjs.add(pad3);
		pads.add(pad3);
		//pad4 = new Pad(40, 250, Pad.STYLE_4, Pad.VERTICAL);
		drawObjs.add(pad4);
		pads.add(pad4);

		//ball = new Ball(200 + 12.5f, 350 + 12.5f, Ball.STYLE_1);	
	}

	public void checkForCollisions() {

		// check collision with pad
		for (Pad pad : pads) {
			this.ball.intersect(pad);
		}

		// check collision with table
		this.ball.intersect(table);
	}

	public void update() {
		ball.update();
		if(ball.isHasCollided()){
			calculateScore();
			this.serverView.updateScore();
			ball.setHasCollided(false);
		}
	}
	
	public void calculateScore(){
		switch (ball.getImpactX()) {
		case 49: //impact pad2
			ball.setPreviousPlayer(player2);
			return;
		case 526: //impact pad4
			ball.setPreviousPlayer(player4);
			return;
		case 25: //impact player2 table side
			player2.decScore();
			if(ball.getPreviousPlayer() != player2){
				ball.getPreviousPlayer().incScore();
			}
			return;
		case 550: //impact player4 table side
			player4.decScore();
			if(ball.getPreviousPlayer() != player4){
				ball.getPreviousPlayer().incScore();
			}
			return;
		default:
			break;
		}
		switch (ball.getImpactY()) {
		case 526: //impact pad1
			ball.setPreviousPlayer(player1);
			return;
		case 49: //impact pad3
			ball.setPreviousPlayer(player3);
			return;
		case 550: //impact player1 table side
			player1.decScore();
			if(ball.getPreviousPlayer() != player1){
				ball.getPreviousPlayer().incScore();
			}
			return;
		case 25: //impact player3 table side
			player3.decScore();
			if(ball.getPreviousPlayer() != player3){
				ball.getPreviousPlayer().incScore();
			}
			return;
		default:
			break;
		}
		
		//If x is not in the values above
		//And ball does not impact the edges
		if (ball.getImpactX() != 50 && ball.getImpactY() != 525
				&& ball.getImpactY() != 50 && ball.getImpactY() != 525) {
			//impact pad1
			if(ball.getImpactY() > 526){
				ball.setPreviousPlayer(player1);
				return;
			}
			//impact pad2
			if(ball.getImpactX() < 49){
				ball.setPreviousPlayer(player2);
				return;
			}
			//impact pad3
			if(ball.getImpactY() < 49){
				ball.setPreviousPlayer(player3);
				return;
			}
			//impact pad4
			if(ball.getImpactX() > 256){
				ball.setPreviousPlayer(player4);
				return;
			}
		}
	}

	public synchronized void drawBuffer() {
		Graphics2D bufferGraphic = bufferedImage.createGraphics();
		for (DrawableObject obj : drawObjs) {
			bufferGraphic.drawImage(obj.getImage(), obj.getX(), obj.getY(),
					null);
		}
		bufferGraphic.drawImage(ball.getImage(), (int) ball.getX(),
				(int) ball.getY(), null);
		bufferGraphic.drawString(ball.toString() + moveLeft + moveRight, 200, 200);
		/*bufferGraphic.drawString("Player 1:" + player1.getScore() + "###"
				+ "Player 2:" + player2.getScore() + "###" + "Player 3:"
				+ player3.getScore() + "###" + "Player 4:" + player4.getScore()
				+ "###", 200, 300);*/
		bufferGraphic.dispose();
	}

	public void drawScreen() {
		Graphics2D screenGraphic = (Graphics2D) this.getGraphics();
		screenGraphic.drawImage(bufferedImage, 0, 0, this);
		Toolkit.getDefaultToolkit().sync();
		screenGraphic.dispose();
	}

	public void startGame() {
		initialize();
		while (isPlaying()) {
			try {
				checkForCollisions();
				update();
				drawBuffer();
				drawScreen();
				Thread.sleep(TIME_SLEEP);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}	
	
	private KeyAdapter keyAdapter = new KeyAdapter() {
		
		@Override
		public void keyPressed(KeyEvent key) {
			switch (key.getKeyCode()) {
			case KeyEvent.VK_RIGHT:
				moveRight = true;
				pad3.setMoveRight(true);
				break;
			case KeyEvent.VK_LEFT:
				moveLeft = true;
				pad3.setMoveLeft(true);
				break;
			default:
				break;
			}
		};
		 
		@Override
		public void keyReleased(KeyEvent e) {
			switch (e.getKeyCode()) {
			case KeyEvent.VK_RIGHT:
				moveRight = false;
				pad3.setMoveRight(false);
				break;
			case KeyEvent.VK_LEFT:
				moveLeft = false;
				pad3.setMoveLeft(false);
				break;
			default:
				break;
			}
		};
	};

	@Override
	public void run() {
		this.isPlaying = true;
		this.startGame();
	}	
	
	public void stop(){
		this.setIsPlaying(false);
	}
	
	public synchronized boolean isPlaying() {
		return this.isPlaying;
	}

	public synchronized void setIsPlaying(boolean b) {
		this.isPlaying = b;
	}
	
}
